
var VueRouter = function({routes}){
    //把用户传递的路径配置数组保存到router实例上
    this.routes = routes;
    //定义路由数据  当前路径默认是hash路径，如果当前没有hash的话会使用/作为默认路径
    let data = {currentPath:window.location.hash.slice(1)||'/'};
    //把data对象变成响应式的,变成响应式之后更新currentPath就可以刷新组件
    Vue.observable(data);
    //把$route也变成响应式的
    this.$route = Vue.observable({
        fullPath:'',//完整路径
        hash:'',    //hash值
        path:'',    //路径对象
        params:{},  //路径参数对象
        query:{}    //查询参数对象
    })
    //属性代理 当访问router.currentPath的时候，就会读取data上的currentPath
    Object.defineProperty(this,'currentPath',{
        get(){//返回data里的当前路径
            return data.currentPath;
        },
        set(newCurrentPath){//当给router赋currentPath值的时候，是赋给data.currentPath
            data.currentPath=newCurrentPath;
        }
    });
    //当hash值发生变化的时候，获取最新的hash值并赋给this.currentPath
    window.addEventListener('hashchange',()=>this.currentPath=window.location.hash.slice(1));
    //设置默认的hash值，如果原来有hash,其实不变，如果原来没有hash,添加/hash
    window.location.hash=this.currentPath;
    //在初始的时候先更新一次Route
    this.updateRoute();
}
VueRouter.prototype.updateRoute = function(){
      //遍历routes数组，进行查找
      this.routes.find(route=>{
        //把route中的path编译 成正则，并提取里面的参数名称
        //路径参数的名称数组paramNames=['user_id','post_id']
        let {regexp,paramNames} = pathToRegexp(route.path);
        //VueRouter实例的currentPath就是完整路径
        this.$route.fullPath = this.currentPath;
        //获取路径名和查询参数
        let [path,querystring] = this.currentPath.split('?');
        //这是路径名，也就是不包括查询参数的路径部分
        this.$route.path = path;
        //查找#的索引
        let hashIndex = querystring?querystring.indexOf('#'):-1;
        //如果存在hash,说明有hash值
        if(hashIndex!==-1){
            //把hash值部分提取出来赋给this.$route.hash
            this.$route.hash = querystring.slice(hashIndex);
            //真正查询字符串就把hash删除掉 a=1&b=2&c=3
            querystring = querystring.slice(0,hashIndex);
        }
        //创建一个空的查询参数对象
        this.$route.query = {};
        //对查询 字符串进行分割，
        (querystring||'').split('&').forEach(pair=>{
            //对每一项再用=进行分割
            let [key,value] = pair.split('=');
            //给query对象赋值a=1,b=2,c=3
            this.$route.query[key]=value;
        });
        //用路径名和正则进行匹配，获取匹配的结果 ，路径参数匹配到的值放在paramValues数组里
        //paramValues=[100,1000]
        let matched = path.match(regexp);
        if(matched){
            let [ ,...paramValues]= matched;//[,100,1000]
            this.$route.params = {};
            //把参数名和参数值赋给this.$route.params
            for(let i=0;i<paramNames.length;i++){
                this.$route.params[paramNames[i]]=paramValues[i];
            }
        }
        return matched;
    });
}
//<a href="#/user" class="router-link-exact-active router-link-active">User</a>
Vue.component('router-link',{
    functional:true,//这是一个函数式组件
    render(createElement,context){//通过 render方法返回虚拟DOM
        let slots = context.slots();//执行slots方法返回slots的对象
        let {to} = context.props;
        //先从context获取到父Vue实例，实例可以获取Vue原型上的$router属性
        let currentPath = context.parent.$router.currentPath;
        let classNames = {
            'router-link-active':currentPath.startsWith(to),
            'router-link-exact-active':currentPath===to,
        }
        return createElement('a',{//返回创建的虚拟DOM，类型是一a标签，属性href=#/user
            class:classNames,
            attrs:{href:`#${context.props.to}`}//给router-link传递的儿子会成为a的儿子
        },slots.default);//slots.default,context.children 给组件传的或者内容就会放在默认插槽中
    }
});
function pathToRegexp(path){
    let paramNames= [];
    let regexpSource = path.replace(/:([^/]+)/g,(_,key)=>{
        paramNames.push(key);
        return `([^\/#\?]+?)`;
    });
    let regexp = new RegExp("^"+regexpSource+"$",'i');
    return {
        regexp,
        paramNames
    }
}
//路由渲染的出口，匹配的路径组件会渲染在这里
Vue.component('router-view',{
    functional:true,
    render(createElement,context){
        //从Vue路由实例获取当前的路径和路由配置数组
        let {currentPath,routes} = context.parent.$router;
        //在路由配置数组中查找与当前路径相等的配置项
        let matched = routes.find(route=>{
           //获取当前的路径配置的路径
           //let {path} = route;//"/user/:user_id/post/:post_id"
           //把路径转化成正则表达式
           let {regexp} = pathToRegexp(route.path);
           //判断此正则表达式和路径是否匹配
           let [path] = currentPath.split('?');
           return regexp.test(path);
        });
        
        //如果找到了，则返回此路由配置项对应的虚拟DOM，进行渲染
        if(matched){
            return createElement(matched.component);
        }
        //如果找不到，则什么不渲染
        return null;
    }
});
//使用一个Vue插件
VueRouter.install = function(Vue){
    //给Vue添加一个混入
    Vue.mixin({
        //以后不管在哪里创建Vue实例，或者Vue组件实例的时候都会应用这个钩子
        beforeCreate(){
            //把选项中的router实例放在Vue的原型对象上
            //以后所有的Vue实例都可以获取到原型上的$router属性
            if(this.$options.router && !Vue.prototype.$router){
                Vue.prototype.$router = this.$options.router;
                Vue.prototype.$route = this.$options.router.$route;
            }
        }
    });
}
